<?php
/**
 * activitysummary.php
 * Get a summary of new pages, comments and users.
 *
 * This is both accessible from the browser and can be used from the
 * command line with the Klenwell cli.php script.  Command line usage:
 *
 *  # php cli.php -c CONFIG_KEY PAGE
 *  $ php cli.php -c example.com ActionActivitySummary
 *
 * References
 *      http://klenwell.com/is/ProjectWikka
 *      http://docs.wikkawiki.org/UsingActions
 *
 * @package     Actions
 * @version     $Id$
 * @license     GNU General Public License v3
 * @link        http://www.opensource.org/licenses/gpl-3.0.html
 *
 * @uses        Wakka::LoadAll()
 * @uses        Wakka::LoadSingle()
 * @uses        Wakka::GetConfigValue()
 *
 * @input       TBA
 * @output      TBA
 */
require_once 'libs/action.class.php';

class ActivitySummaryAction extends WikkaAction {

    var $version = '0.9.1';

    # parameter defaults
    var $interval = 24;  # hours
    var $activepages = 5;
    var $emailto = '';

    # internal
    var $MailingList = array();
    var $sec_diff = 0;
    var $datestampf = 'Y-m-d H:i:s';
    var $report_period_start = '';
    var $report_period_end = '';

    function action_set_up() {
        if ( $this->has_param('interval') ) {
            $this->interval = (int) $this->get_param('interval');
        }
        if ( $this->has_param('activepages') ) {
            $this->activepages = (int) $this->get_param('activepages');
        }
        if ( $this->has_param('emailto') ) {
            $this->emailto = $this->get_param('emailto');
        }
        else {
            $this->emailto = $this->get_config('admin_email');
        }

        # set mailing list
        $this->MailingList = $this->_set_mailing_list($this->emailto);

        $this->sec_diff = $this->interval * 60 * 60;
        $this->report_period_end = date($this->datestampf);
        $this->report_period_start = date($this->datestampf,
            time() - $this->sec_diff);
    }

    function main() {
        $this->action_set_up();
        $activity_report = $this->_build_activity_report();
        $active_pages = $this->_build_active_pages();
        $content = $this->_build_output($activity_report, $active_pages);
        $content = $this->_mail_report($content);
        return $content;
    }

    function output($content) {
        print $content;
    }

    /**
     *  Mail report if this is a command line request.
     *  If not cli, immediately returns content intact.
     */
    function _mail_report($content) {

        if ( ! $this->is_cli ) {
            return $content;
        }

        $subject = sprintf('Wikka Summary Report for %s: %s to %s',
                           $this->get_config('wakka_name'),
                           $this->report_period_start,
                           $this->report_period_end );
        $body = $content;

        $StdOut = array("\n\n-----\n", 'mailing report...');

        if ( $this->mail($this->MailingList, $subject, $body) ) {
            $StdOut[] = sprintf("[OK] report mailed to: %s\n",
                                implode(', ', $this->MailingList));
        }
        else {
            $StdOut[] = sprintf("[FAIL] see error message\n");
        }

        $content .= implode("\n", $StdOut);
        return $content;
    }

    function _build_output($activity_report, $active_pages) {
        if ( $this->is_cli ) {
            return $activity_report;
        }

        if ( $this->is_admin &&
             $this->PageAcl['write'] != $this->AclAllow['admins'] ) {
            $warning = sprintf('<em class="error">%s (ACL setting: %s)</em>',
                               'It is recommended you set page ACL for write to admin only',
                               $this->AclAllow['admins']);
        }
        else {
            $warning = '';
        }

        $template = <<<XHTML
<h2>Wikka Activity Summary</h2>
%s
<hr />
%s
<hr />
%s
<hr />
XHTML;
        return sprintf($template, $warning, $activity_report, $active_pages);
    }

    function _build_active_pages() {
        if ( $this->is_cli ) {
            return '';
        }

        $template = <<<XHTML
<div class="active_pages">
%s
</div>
XHTML;

        $ActivePages = $this->_find_active_pages();
        $row_f = '~- %s (%s edit%s)';
        $PageList = array();

        foreach ( $ActivePages as $Page ) {
            $_s = ( $Page['edits'] == 1 ) ? '' : 's';
            $PageList[] = sprintf($row_f, $Page['tag'], $Page['edits'], $_s);
        }

        if ( !empty($PageList) ) {
            $page_list = implode("\n", $PageList);
        }
        else {
            $page_list = 'no page edits during this period';
        }

        $page_list_f = "====Most Active Pages (last %s hours)====\n%s";
        $page_list = $this->Wikka->format(sprintf($page_list_f,
                                                  $this->interval,
                                                  $page_list));

        return sprintf($template, $page_list);
    }

    function _build_activity_report() {

        if ( ! $this->is_admin && ! $this->is_cli ) {
            return '';
        }

        $NewUsers = $this->_find_new_users();
        $NewPages = $this->_find_new_pages();
        $UpdatedPages = $this->_find_updated_pages();

        $new_user_section = $this->_build_new_user_section($NewUsers);
        $new_page_section = $this->_build_page_update_section($NewPages);
        $updated_page_section = $this->_build_page_update_section($UpdatedPages);

        $reportf = <<<HPLAIN
ACTIVITY SUMMARY
%s to %s (%s hours)

NEW USERS
%s

NEW PAGES
%s

UPDATED PAGES
%s
HPLAIN;
        $content = sprintf( $reportf,
                            $this->report_period_start,
                            $this->report_period_end,
                            $this->interval,
                            $new_user_section,
                            $new_page_section,
                            $updated_page_section );

        $html_t = <<<XHTML
<h3>Current Activity Report</h3>
<pre>%s</pre>
XHTML;

        if ( ! $this->is_cli ) {
            $content = sprintf($html_t, $content);
        }

        return $content;
    }

    function _build_new_user_section($Rows) {
        #return $this->_pr($Rows);
        $ColFormat = array(
            'user' => 24,
            'email' => 36,
            'signup time' => 16,
        );
        $Lines = array();

        if ( empty($Rows) ) {
            return 'no new users';
        }

        # build formatter
        foreach ( $ColFormat as $len ) {
            $RowFormat[] = sprintf('%%-%s.%ss', $len, $len);
        }
        $row_f = implode(' ', $RowFormat);

        $Lines[] = strtoupper(call_user_func_array( 'sprintf',
            array_merge(array($row_f),array_keys($ColFormat)) ));
        foreach ( $Rows as $Row ) {
            $Lines[] = sprintf($row_f, $Row['name'], $Row['email'], $Row['signuptime']);
        }
        return implode("\n", $Lines);
    }

    function _build_page_update_section($Rows) {
        # page, age, user, email, signuptime, blurb
        #return print_r($Rows,1);
        $row_f = '%-20.20s %-10.10s %-16.16s %-10.10s  %-12.12s';
        $Lines = array();

        if ( empty($Rows) ) {
            return 'no new pages';
        }

        $Lines[] = strtoupper(sprintf($row_f, 'page', 'age', 'user',
                                      'signup', 'change'));
        foreach ( $Rows as $Row ) {
            $diff_len = strlen($Row['body']) - strlen($Row['body_before']);
            if ( $diff_len > 0 ) {
                $diff_len = sprintf('+%s', $diff_len);
            }
            $Lines[] = sprintf($row_f, $Row['tag'], $Row['age'], $Row['user'],
                               $Row['signuptime'], $diff_len);
        }
        return implode("\n", $Lines);
    }

    function _find_new_pages()
    {
        $NewPages = array();
        $PageList = $this->_find_recent_pages();

        foreach ( $PageList as $Row ) {
            $tag = $Row['tag'];
            if ( $Row['status'] == 'new' ) {
                $NewPages[$tag] = $Row;
            }
        }

        return $NewPages;
    }

    function _find_updated_pages()
    {
        $UpdatedPages = array();
        $PageList = $this->_find_recent_pages();

        foreach ( $PageList as $Row ) {
            $tag = $Row['tag'];
            if ( $Row['status'] == 'update' ) {
                $UpdatedPages[$tag] = $Row;
            }
        }

        return $UpdatedPages;
    }

    function _find_new_users()
    {
        $users_table = $this->Wikka->GetConfigValue('table_prefix') . 'users';

        $sqlf = <<<HSQL
SELECT
    name, email, signuptime, status
FROM %s U
WHERE 1 = 1
    AND signuptime > DATE_SUB(Now(), INTERVAL %s SECOND)
ORDER BY signuptime DESC
HSQL;

        $ParamList = array($users_table, $this->sec_diff);
        $sql = $this->safe_sql($sqlf, $ParamList);
        $Rows= $this->Wikka->LoadAll($sql);
        return $Rows;
    }

    function _find_recent_pages()
    {
        $pages_table = $this->Wikka->GetConfigValue('table_prefix') . 'pages';
        $users_table = $this->Wikka->GetConfigValue('table_prefix') . 'users';

        $sqlf = <<<HSQL
SELECT
    P.tag, P.time,
    IF(N.oldest < DATE_SUB(Now(), INTERVAL %s SECOND),'update','new') AS status,
    TIMEDIFF(Now(), P.time) AS age,
    P.user, U.email, U.signuptime,
    N.newest, N.oldest,
    P.body,
    P.latest, Now() as now
FROM %s P
LEFT JOIN %s U ON U.name = P.user
LEFT JOIN (
    SELECT tag, MAX(time) AS newest, MIN(time) AS oldest
    FROM %s
    GROUP BY tag
) N ON N.tag = P.tag
WHERE 1 = 1
    AND newest > DATE_SUB(Now(), INTERVAL %s SECOND)
    AND P.time = newest
ORDER BY P.time DESC
HSQL;

        $ParamList = array($this->sec_diff, $pages_table, $users_table,
                           $pages_table, $this->sec_diff);
        $sql = $this->safe_sql($sqlf, $ParamList);
        $Rows = $this->Wikka->LoadAll($sql);
        #printf('<pre>%s</pre>', $sql);
        #printf('<pre>%s</pre>', $this->_pr($Rows));

        # find diffs
        $sqlf = <<<HSQL
SELECT body AS body_before
FROM %s
WHERE time = (
    SELECT MAX(time) FROM %s
    WHERE time < DATE_SUB(Now(), INTERVAL %s SECOND)
    AND tag = '%s'
    GROUP BY tag )
AND tag = '%s'
LIMIT 1
HSQL;

        $PageUpdates = array();
        foreach ( $Rows as $Row ) {
            $ParamList = array($pages_table, $pages_table, $this->sec_diff,
                               $Row['tag'], $Row['tag']);
            $sql = $this->safe_sql($sqlf, $ParamList);
            $Diff = NULL;
            if ( $Diff = $this->Wikka->LoadSingle($sql) ) {
                $Row['body_before'] = $Diff['body_before'];
            }
            else {
                $Row['body_before'] = '';
            }
            $PageUpdates[] = $Row;
            #print_r(array($Row['tag'], strlen($Row['body']), strlen($Row['body_before'])));
        }
        #printf('<pre>%s</pre>', $this->_pr($PageUpdates));
        return $PageUpdates;
    }

    function _find_active_pages()
    {
        $pages_table = $this->Wikka->GetConfigValue('table_prefix') . 'pages';

        $sqlf = <<<HSQL
SELECT
    P.tag, P.time, N.edits,
    P.user as last_editor,
    N.newest, N.oldest, TIMEDIFF(Now(), P.time) AS last_edit,
    P.latest, Now() as now
FROM %s P
LEFT JOIN (
    SELECT tag, MAX(time) AS newest, MIN(time) AS oldest,
        COUNT(tag) AS edits
    FROM %s
    WHERE time > DATE_SUB(Now(), INTERVAL %s SECOND)
    GROUP BY tag
) N ON N.tag = P.tag
WHERE 1 = 1
    AND newest > DATE_SUB(Now(), INTERVAL %s SECOND)
    AND P.time = newest
ORDER BY edits DESC
LIMIT %s
HSQL;

        $ParamList = array($pages_table, $pages_table, $this->sec_diff,
                           $this->sec_diff, $this->activepages);
        $sql = $this->safe_sql($sqlf, $ParamList);
        $Rows = $this->Wikka->LoadAll($sql);
        return $Rows;
    }

    function _set_mailing_list($email_string) {
        $MailingList = array();
        $SplitList = preg_split('%[\,;]%', $email_string);
        foreach ( $SplitList as $email ) {
            $MailingList[] = trim($email);
        }
        return $MailingList;
    }

    function _abort($msg) {
        $m = sprintf('Action Exception: %s', $msg);
        throw new Exception($m);
    }

    function _pr($Array) {
        return print_r($Array,1);
    }
}

# Main Routine
try {
    $Action = new ActivitySummaryAction($this, $vars);
    $content = $Action->main();
    $Action->output($content);
}
catch(Exception $e) {
    printf('<em class="error">%s</em>', $e->getMessage());
}
?>
